Zydecx's Site

Debug code, debug life, debug today!

类型转换与反射(Java)

Time: , by zydecx

类型转换&检查

类型转换

  1. 强制类型转换

    强制类型转换通过(NewType)形式实现类型的强制转换。如下例所示,将aList强制转换为ArrayList类型。

    List aList = new ArrayList();
    ArrayList anArrayList = (ArrayList) aList;
  1. Class.cast()

    这是Class提供的一个类型装的方法,通过该方法可以更灵活的实现类型转换,而不用在代码编写的时候就确定要转换的类的名称。(事实上,该方法使用情况不多,Java SE5类库中仅一处有使用)

    List aList = new ArrayList();
    Class<ArrayList> arrayListClass = ArrayList.class;
    ArrayList anArrayList = arrayListClass.cast(aList);

类型检查

  1. instanceof运算符,左操作数为对象,右操作数为类名
  2. Class.isInstance()方法,参数为对象实例
  3. Class.isAssignableFrom()方法,参数为Class对象

    SuperTest superBean = new SuperTest();
    ExtendTest extendBean = new ExtendTest();

    Class<?> superClass = SuperTest.class;
    Class<?> extendClass = ExtendTest.class;

    System.out.println(extendBean instanceof SuperTest); // true
    System.out.println(superBean instanceof ExtendTest); // false
    System.out.println(extendClass.isInstance(extendBean)); // true
    System.out.println(superClass.isAssignableFrom(extendClass)); // true
    System.out.println(superClass.isAssignableFrom(superClass)); // true

反射

Java的反射通过Class类和java.lang.reflect类库实现。通过它们,可以动态获得类的构造器、方法、数据成员等信息并创建新对象、执行方法和设置数据成员。

获取/修改类的方法和数据成员

通过反射可以获得类的所有构造器、方法和数据成员,即便它们是被封装在类内部(private/protected/default)。因此,使用反射需要考虑,对违反访问权限的操作是否有风险(如日后更新导致封装改变)。

方法的访问与调用

如下例所示,通过反射获得RandomnextInt(int n)方法并执行。

Random rand = new Random();
Method randNextIntMethod = rand.getClass().getDeclaredMethod("nextInt", int.class);
randNextIntMethod.setAccessible(true);
System.out.println(randNextIntMethod.invoke(rand, 10));

域的访问与修改

如下例所示,通过反射读取并修改Thread的私有域threadLocals

Thread thread = Thread.currentThread();
Field threadLocalsField = thread.getClass().getDeclaredField("threadLocals");
threadLocalsField.setAccessible(true);
System.out.println(threadLocalsField.get(thread));
threadLocalsField.set(thread, null);
System.out.println(threadLocalsField.get(thread));

动态代理

代理是基本的设计模式之一,可以在不改变现有类的结构基础上,为接口添加额外的操作,如日志记录,度量方法调用开销等。

对于代理模式,代理的是确定的接口或类,而动态代理则在此基础上,可动态创建代理并调用代理方法,而不必局限于编译时指定的接口或类。一个动态代理的示例如下,它可以记录代理的方法调用的开销。需要注意的是,invoke方法中,传入的proxyDynamicProxy的实例本身,实际调用的是DynamicProxy的数据成员proxied

/**
 * 方法调用开销代理
 */
class DynamicProxy implements InvocationHandler {
    private Object proxied;
    public DynamicProxy(Object proxied) {
        this.proxied = proxied;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("** proxy:" + proxied.getClass() + ", method:"
                + method.getName() + ", parameter types:"
                + Arrays.toString(method.getParameterTypes()));
        long startTime = System.nanoTime();
        
        Object result =  method.invoke(proxied, args);
        
        long endTime = System.nanoTime();
        System.out.printf("   cost time: %f ms\n", (endTime - startTime)/10e6);
        
        return result;
    }
}

class DynamicProxyDemo {
    public static void main(String[] args) {
        List<String> aList = new ArrayList<>();
        List<String> listProxy = (List<String>) Proxy.newProxyInstance(
                List.class.getClassLoader(), new Class[] { List.class },
                new DynamicProxy(aList));
        listProxy.add("add a string");
        listProxy.get(0);
    }
}

/**Output:
** proxy:class java.util.ArrayList, method:add, parameter types:[class java.lang.Object]
   cost time: 0.030873 ms
** proxy:class java.util.ArrayList, method:get, parameter types:[int]
   cost time: 0.001478 ms
*/

空对象

使用空对象代替null,可以减少许多对null检查的代码,提高代码优雅性。当然,仍然需要对空对象的检查。可以通过创建一个空的Null接口,将实现该接口的对象作为空对象使用。典型的,

  • 若为一个类创建空对象,则创建一个该类的子类,子类实现Null接口,将子类对象作为该类的空对象

    将空对象作为单例使用(final static),可以通过==代替Null接口检查,判断一个对象是不是空对象。

  • 若为一系列实现公共接口的类创建空对象,则通过动态代理,创建一个实现了公共接口和Null接口的类,将该类对象作为空对象。特别地,为了标注空对象属于哪个实现类,可通过在空对象中添加实现类的类型数据,方便辨识。


This is a magic phrase. You CANNOT see it(I'll really FULE you if you do that), but it does work. Why? You may feel confused. OK, at least it doesn't afftect your experience and it works. That is what we call MAGICE!